package com.zeus.core.rule;

import com.google.common.util.concurrent.RateLimiter;
import com.zeus.common.constants.ZeusConfigConstant;
import com.zeus.common.enums.ZeusAlarmLevelEnum;
import com.zeus.core.context.ZeusContext;
import com.zeus.core.config.data.ZeusConfig.AlarmLevelRule;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 聚合规则-标准
 * 1、根据关键词区分报警等级
 * 2、通过使用限流，判断是否发送报警
 *
 * @author f1ybear_wang  2022/3/3 3:18 下午
 */
public class GatherRuleNormal extends AbstractGatherRule {
    /**
     * 1分钟，60秒
     */
    private static final double ONE_MINUTES_TO_SECONDS = 60d;

    /**
     * 1分钟，60000毫秒 
     */
    private static final double ONE_MINUTES_TO_MILLISECONDS = 60000d;

    /**
     * V1聚合方式使用，限流
     */
    private RateLimiter rateLimiter;

    /**
     * 限流是否初始化完成
     */
    private boolean rateLimiterInited;

    public GatherRuleNormal() {
        super();
    }

    @Override
    public void addTaskBefore() {
        // default
    }

    @Override
    public boolean isTimeout() {
        Long lastDateTime = this.lastDate;
        if (lastDateTime == null) {
            return false;
        }
        // 超过一分钟，删除
        return ONE_MINUTES_TO_MILLISECONDS < (System.currentTimeMillis() - lastDateTime);
    }

    @Override
    public boolean isCanSendReport() {
        if (this.rateLimiter == null) {
            return true;
        }
        return this.rateLimiter.tryAcquire();
    }

    @Override
    public void setAlarmLevel(ZeusAlarmLevelEnum alarmLevel) {
        this.levelEnum = Optional.ofNullable(levelEnum).orElse(ZeusAlarmLevelEnum.UN_KNOW);
        initRateLimiter();
    }

    @Override
    public void setAlarmLevel(String... keyWorlds) {
        List<AlarmLevelRule> levelRuleList =
            Optional.ofNullable(ZeusContext.getContext().getLevelRule()).orElse(new ArrayList<>());
        AlarmLevelRule alarmLevelRule = levelRuleList.stream()
            .filter(e -> e != null && !ZeusAlarmLevelEnum.UN_KNOW.getCode().equals(e.getAlarmLevel()))
            .filter(item -> checkLogContentInRule(item.getIncludeKeyWordList(), keyWorlds)).findFirst().orElse(null);

        Integer rate = null;
        if (alarmLevelRule == null) {
            levelEnum = ZeusAlarmLevelEnum.UN_KNOW;
        } else {
            this.levelEnum = Optional.ofNullable(ZeusAlarmLevelEnum.parse(alarmLevelRule.getAlarmLevel()))
                .orElse(ZeusAlarmLevelEnum.UN_KNOW);
            rate = alarmLevelRule.getRate();
        }

        // 初始化
        initRateLimiter(computeQps(rate));
    }

    private boolean checkLogContentInRule(Set<String> rule, String... keyWorlds) {
        if (rule == null || rule.isEmpty() || keyWorlds == null || keyWorlds.length == 0) {
            return false;
        }
        return rule.stream().filter(Objects::nonNull)
            .anyMatch(e -> Arrays.stream(keyWorlds).filter(Objects::nonNull).anyMatch(item -> item.contains(e)));
    }

    private void initRateLimiter() {
        double qps = ZeusConfigConstant.DefaultValue.DEFAULT_RATE;
        if (this.levelEnum != null) {
            List<AlarmLevelRule> levelRuleList =
                Optional.ofNullable(ZeusContext.getContext().getLevelRule()).orElse(new ArrayList<>());

            AlarmLevelRule alarmLevelRule = levelRuleList.stream().filter(Objects::nonNull)
                .filter(e -> this.levelEnum.getCode().equals(e.getAlarmLevel())).findFirst().orElse(null);
            if (alarmLevelRule != null) {
                qps = computeQps(alarmLevelRule.getRate());
            }
        }
        initRateLimiter(qps);
    }

    private void initRateLimiter(double qps) {
        if (this.rateLimiterInited) {
            return;
        }
        this.rateLimiter = RateLimiter.create(qps, 1L, TimeUnit.MINUTES);
        this.rateLimiterInited = true;
    }

    /**
     * 计算QPS
     *
     * @param rate 每分钟的报警次数
     * @return 每秒钟报警次数
     * @author wangfeixiong126@gmail.com  2021/6/24 1:55 上午
     */
    private double computeQps(Integer rate) {
        if (rate == null) {
            return ZeusConfigConstant.DefaultValue.DEFAULT_RATE;
        }
        // 计算每秒
        return rate.doubleValue() / ONE_MINUTES_TO_SECONDS;
    }
}
